配置ipv4 ipv6优先级

·

linux系统中的ip选择

在 Linux 系统中,默认情况下,许多应用(尤其是使用 glibc 的 getaddrinfo() 的程序,如 curl、wget、ssh 等)会遵循 RFC 6724 的地址选择策略,优先选择 IPv6(若 AAAA 记录存在且网络支持),即使 IPv4 可用。

要让系统/应用优先使用 IPv4,再回退到 IPv6,有以下几种常用方法(按推荐程度排序):

修改 gai.conf(全局、标准、推荐)

这是最标准、影响范围可控的方式,只影响使用 getaddrinfo() 的应用。

sudo vim /etc/gai.conf
# 优先 IPv4(将 ::ffff:0:0/96 的优先级调高)
# precedence ::ffff:0:0/96  100
sudo sed -i 's/#precedence ::ffff:0:0\/96  10/precedence ::ffff:0:0\/96  100/' /etc/gai.conf

原理:::ffff:0:0/96 是 IPv4-mapped IPv6 地址(如 ::ffff:192.0.2.1)。提高其优先级后,当域名同时有 A 和 AAAA 记录时,getaddrinfo() 会优先返回 IPv4 地址。

# 无需重启,新进程立即生效。
getent ahostsv4 example.com | head -1   # 应返回 IPv4
getent ahostsv6 example.com | head -1   # 应返回 IPv6
# 测试解析顺序(注意顺序):
python3 -c "import socket; print(socket.getaddrinfo('example.com', 80, socket.AF_UNSPEC, socket.SOCK_STREAM))"

环境变量(用户级/临时)

适用于特定用户或会话,不影响系统全局:

export GAI_CONF=/dev/null  # 禁用 gai.conf(回退到旧行为,常优先 IPv4)
# 或更精准:
export GAI_CONF=<(echo "precedence ::ffff:0:0/96 100")

修改内核地址选择策略(推荐,行为最接近“IPv4 first”)

Linux 使用 RFC 3484 / RFC 6724 地址选择规则,可以通过 ip addrlabel 调整优先级,让 IPv4 地址的地址标签优先级更高。

  • ➤ 查看当前地址标签:
ip addrlabel list
# 通常看到:
# prefix ::ffff:0.0.0.0/96 label 4
# prefix ::/0                label 1

其中 ::ffff:0.0.0.0/96 是 IPv4-mapped IPv6 地址。

  • ➤ 让 IPv4-mapped 更优先:
sudo ip addrlabel add prefix ::ffff:0.0.0.0/96 label 0
# 让它的 label 更小 → 优先级更高。
  • ➤ 或者反过来降低 IPv6 的优先级:
sudo ip addrlabel add prefix ::/0 label 10

不推荐的方法(副作用大)

  • 禁用 IPv6(如 sysctl net.ipv6.conf.all.disable_ipv6=1):
    • 虽可强制走 IPv4,但破坏 IPv6 功能,不符合现代网络趋势,且某些服务依赖 IPv6 localhost(如部分 Docker/LXC 配置),可能导致意外故障。
  • 修改 /etc/hosts:
    • 手动将域名映射到 IPv4 地址仅对特定主机名有效,不具普适性。

addrlable.conf 和gai.conf 的区别

/etc/iproute2/addrlabel.conf 和 /etc/gai.conf 都涉及 IP 地址优先级/排序,但它们的工作层级、目标、机制截然不同。下面我们从原理、作用域、典型场景三方面对比:

维度/etc/gai.conf/etc/iproute2/addrlabel.conf
标准依据RFC 6724RFC 6724 §2.1 + Linux 内核实现
作用对象✅ 用户空间应用(通过 getaddrinfo()):
curl, wget, ssh, python socket, systemd-resolved 等
✅ 内核空间(net/ipv6/addrlabel.c):
影响 内核生成的源地址标签(label),供 getaddrinfo() 读取参考
控制什么应用层 地址排序规则(getaddrinfo() 的输出顺序)内核为每个 IPv6 地址分配的 label 值(ip addrlabel list 可见),是 gai.conf 中 label 规则的数据源
是否必须配合使用❌ 可独立工作(可仅靠 precedence 规则)⚠️ 通常需配合 gai.conf 的 label 规则才生效;单独修改它不直接改变应用行为
典型配置项precedence, label, scopev4prefix label(如 ::1/128 0)
生效方式应用启动时读取(或按 GAI_CONF 环境变量)ip addrlabel 加载;内核维护 label 表;getaddrinfo() 查询该表

/etc/gai.conf:用户空间的“决策引擎”